# Transacciones ## Definición de transacciones Una transacción es un **conjunto de operaciones** sobre la base de datos que han de ejecutarse todas en conjunto. Es decir, **o se ejecutan todos o no se ejecuta ninguna**. ### Comienzo y fin de una transacción Las transacciones comienzan implícitamente **con la primera operación SQL** que se ejecute (INSERT-DELETE-UPDATE-SELECT). Comandos DDL como CREATE TABLE también, pero estos no requieren de un COMMIT para que funcionen. En Oracle, los comandos DDL provocan un COMMIT antes de procesarse) En Oracle, un cierre de sesión voluntario, realizará un COMMIT implícito, los cambios de la transacción incompleta se guardarán. Una interrupción de la sesión, sin finalizar una transacción en curso, provocará un ROLLBACK implícito, descartando los cambios. ## Autocommit El **autocommit** es una propiedad de una sesión de la base de datos. Puede ser verdadera o falsa. El autocommit tratará cada operación SQL como una transacción por separado. En Oracle, desactivamos el autocommit con el comando `set autocommit off` # ACID Las bases de datos deben soportar las características ACID: ## Atomicidad Las operaciones se agrupan en una misma transacción, todo indivisible. **O todas o ninguna. **La transacción solo puede acabar con **commit** o **rollback**. ## Consistencia La base de datos debe mantenerse consistente. Al agrupar las operaciones en una transacción, nos aseguramos que los cambios se realicen de todas las operaciones, evitando que se haga una modificación a medias. ## Aislamiento (Isolation) Al acceder a la base de datos, debemos tener la sensación virtual de que estamos accediendo nosotros solamente. Se debe evitar leer cambios que se estén realizando en otra transacción en curso. Este parámetro puede configurarse para ser más estricto o no. ## Durabilidad Los cambios deben persistir en la base de datos. Estos deben guardarse de manera permanente, para evitar perdidas de información en caso de fallo, las bases de datos cuentan con historiales de registro (logs), que persisten a las operaciones y permiten reconstruir los cambios en caso de error. # Soporte del Aislamiento en SQL Los tres síntomas de falta de aislamiento principales son: ## Lectura sucia Ocurre cuando puedes leer cambios antes de que la sesión haya realizado un **COMMIT**. ## Lectura no repetible Ocurre cuando con una misma **SELECT**, obtienes dos resultados distintos desde la misma sesión. Esto se debe a que se han leído los cambios o borrados producidos por otra transacción commiteada. ## Lectura fantasma Ocurre cuando al realizar dos consultas u operaciones DML, se obtienen nuevas filas. Esto es debido a las inserciones realizadas por otras transacciones commiteadas. ### Niveles de Aislamiento SQL | Nivel de aislamiento SQL | Lectura sucia | Lectura no repetible | Lectura fantasma | |:-------------------------|:--------------|:---------------------|:-----------------| | READ UNCOMMITED | SI | SI | SI | | READ COMMITED | NO | SI | SI | | REPEATABLE READ | NO | NO | SI | | SERIALIZABLE | NO | NO | NO | El nivel por defecto en Oracle es **Read Commited**. Podemos modificar el nivel de aislamiento de una **transacción individual** en Oracle con el comando `SET TRANSACTION ISOLATION LEVEL SERIALIZABLE`   Podemos modificar el nivel de aislamiento de toda la sesión en Oracle con el comando `ALTER SESSION SET ISOLATION\_LEVEL=SERIALIZABLE` # Aislamiento de Instantánea y Concurrencia Multiversión Se conoce por MVCC el control de concurrencia multiversión. Cada operación de escritura sobre un dato D, genera una nueva versión del dato D' y a la vez guarda la versión original D sobre la que se ha aplicado la escritura. Los protocolos multiversión se basan en asignar una marca de tiempo a cada transacción La variante del protocolo multiversión que solo guarda la marca de tiempo de la transacción que genera la versión es el llamado aislamiento de instantánea. ## Aislamiento de instantáneas Se dice que T' es una transacción concurrente con T, si en el momento de inicio de T, T' aún no ha terminado. En el momento de inicio de la transacción registramos un **ts\_inicio(T) **y el momento final de T, que denotaremos como **ts\_fin(T).** ## Generación de versiones Las tabla de versiones, debe contar mínimo con tres columnas con la siguiente información: **Identificador de la fila** que corresponde a su versión, un **booleano borrado**, que permita saber si la versión ha sufrido un **DELETE** y el **ts\_inicio** de la transacción que creó la versión. ### Como se generan nuevas versiones Se pueden crear nuevas versiones de los datos o en se añaden nuevas filas a la tabla de versiones cuando ocurren las siguientes acciones: 1. Se inserta una fila nueva. Esta se etiqueta con la **ts\_inicio** de la transacción que lo insertó. 2. Se modifica una fila insertada. Los datos modificados, se etiquetan con la **ts\_inicio** de la transacción que lo modificó. 3. Se borra una fila insertada. Los datos borrados, también crean una nueva fila en la tabla de versiones, con el **boolean de borrado** en **TRUE**. Si el dato que se ha borrado ya existía en la **tabla de versiones**, se modifica el campo del **boolean** en esa fila. ## Creación de la instantánea La tabla de transacciones, es necesario que registre la siguiente información sobre cada una de las transacciones:   1. Su estado: **Activa, Cometida, Retrocedida**   2. Su **ts\_inicio** y su **ts\_fin**. El **ts\_fin** es *null*, si la transacción está activa.   3. El **nivel de aislamiento**. Cuando se crea una instantánea, la transacción debe acceder a la versión adecuada de cada fila. 1. Si existe una versión generada por T (coincide el **ts\_inicio** entre la versión y la transacción). Se devuelve esa versión. 2. En caso contrario, evaluamos las versiones candidatas: 1. Las versiones deben corresponder a una transacción en estado **Cometido**. 2. En caso de que el nivel de aislamiento fuera **Serializable**, también se requiere que la versión sea de una transacción que haya empezado y terminado antes de que comenzara la transacción que está leyendo. Finalmente, se elige la versión más reciente. En MVCC, una transacción siempre puede ver sus propias escrituras **no comiteada**, porque esa versión lleva su mismo **ts\_inicio**. ## Mantenimiento de la Serializabilidad El aislamiento de instantánea (MVCC), puede resolver los conflictos lectura-escritura, pero no puede resolver los conflictos escritura-escritura. ### Resolver conflictos escritura-escritura 1. **Primer commit gana** - Cuando una transacción hace **COMMIT**, se comprueba si otra transacción concurrente ya ha hecho commit sobre el mismo dato. - Si hay solapamiento → **rollback implícito y error** 2. **Primera actualización gana**   - Se mantiene un bloqueo de escritura sobre los datos modificados hasta el final de la transacción.   - El conflicto lo gana quien bloquea primero. | Situación | Comportamiento | |:-------------------------------------------|:------------------------------------------------------------------------------------------------------------------------------| | **Caso 1**: El dato no está bloqueado | T adquiere el bloqueo. Si detecta que una T' concurrente ya hizo commit sobre ese dato → **rollback de T** | | **Caso 2**: El dato está bloqueado por T' | T espera. Si T' acaba con commit → **rollback de T**. Si T' acaba con rollback → T **adquiere el bloqueo y continúa.** | ## Otros conflictos ### Conflictos de escritura con distintos niveles de aislamiento - Solo las transacciones **SERIALIZABLE** comprueban los conflictos de escritura. - Si una transacción es **SERIALIZABLE**, aplica primera actualización gana frente a cualquier otra transacción concurrente, independientemente del nivel de aislamiento de esta. - Una transacción **READ COMMITTED** nunca provoca un rollback por conflicto de escritura. ### Conflicto al borrar la misma fila - Si dos transacciones leen una fila y ambas deciden borrarlas en función de su valor. - La segunda en intentar el **DELETE** espera y, si la primera hizo commit, recibe el error de serialización. ### Conflicto entre campos distintos de la misma fila - Aunque dos transacciones modifiquen campos diferentes de la misma fila, los **SGBDs** aplican granularidad de fila (no de campo). - **Resultado**: se genera el mismo error de serialización **(ORA-08177 - Can't Serialize)** - Esto evita el problema de la actualización perdida en aplicaciones que hacen **UPDATE** de toda la fila. ### Tratamiento de la actualización perdida en READ COMMITTED Cuando no se puede usar SERIALIZABLE, hay dos alternativas: - **Bloqueo Pesimista**: usar `SELECT … FOR UPDATE` para bloquear la fila antes de que el usuario la edite. Garantiza que nadie más la modifica mientras el usuario trabaja. - **Bloqueo Optimista**: no se bloquea al leer. En el momento del **UPDATE** se comprueba (mediante un campo de versión o timestamp) si la fila ha cambiado desde que se leyó. Si cambió → **se informa al usuario y se reintenta.** # Anexo > SGBD significa Sistema de Gestión de Bases de Datos: Oracle, PostgreSQL, SQL Server…